use crate::StateManagement;
use crate::utils::git;
use crate::utils::logger;
use anyhow::Result;
use console::style;
use dialoguer::Input;
use rust_embed::RustEmbed;
use std::path::PathBuf;
use std::time::Duration;
use update_informer::{registry, Check};
use crate::UIDesign;
use crate::CSSPreProcessors;
use crate::Hook;
use crate::content::update_package_json::{update_project_basic_info,add_dependency_to_package_json};
use crate::content::add_customize_hook::copy_hook_to_project;
use crate::content::update_state_management::update_state_management_by_choose;

#[derive(RustEmbed)]
#[folder = "template-react"]
struct Asset;

pub enum DependenciesMod {
    Dev,
    Prod
}

pub struct CreationOptions {
    pub state:StateManagement,
    pub _hook: Hook,
    pub css: CSSPreProcessors,
    pub ui:UIDesign,
    pub cli_mode: bool,
}

/**
 * 核心代码
 */
/// 该函数接受两个参数：project_name 表示项目的名称，creation_options 包含一些创建选项。
/// 函数的目标是在指定的目录中创建一个项目，并执行一系列操作，
/// 1. 删除已存在的目录内容（如果目录已存在）、
/// 2. 创建项目目录结构、
/// 3. 复制项目文件、
/// 4. 修改 package.json 文件，
/// 5. 初始化 Git 仓库。
pub fn create(project_name: &str, creation_options: CreationOptions) -> Result<()> {
    // 初始化项目目录路径
    let project_dir: PathBuf = PathBuf::from(project_name);
    logger::command_tip("创建项目目录...");
    match std::fs::create_dir_all(&project_dir) {
        Ok(_) => {}
        Err(err) => logger::exit_error("std::fs::create_dir_all():", err),
    }

    logger::command_msg("cli 开始工作");

    /*
      复制template_xx 文档
    */
    for filename in Asset::iter() {
        let file_contents = Asset::get(filename.as_ref()).unwrap();
        let mut file_path = std::path::PathBuf::from(&project_dir);
        file_path.push(filename.as_ref());
        let mut directory_path = std::path::PathBuf::from(&file_path);
        directory_path.pop();

        logger::add_file_msg(filename.as_ref());
        std::fs::create_dir_all(directory_path)?;
        std::fs::write(file_path, file_contents.data)?;
    }
    
    // 修改package.json的基础信息
    update_project_basic_info(&project_dir,project_name.to_owned())?;

    // 处理 UI 组件库
    let ui = creation_options.ui;
     add_dependency_to_package_json(
        &project_dir,
        match ui {
            UIDesign::Antd => "antd",
            UIDesign::Arco => "@arco-design/web-react",
        },
        match ui {
            UIDesign::Antd => "^5.12.7",
            UIDesign::Arco => "^2.57.1",
        },
        DependenciesMod::Prod
    )?;
    // 引入css 预处理器
    let css = creation_options.css;
    add_dependency_to_package_json(
        &project_dir,
        match css {
            CSSPreProcessors::Sass => "sass",
            CSSPreProcessors::Less => "less",
            CSSPreProcessors::Stylus => "stylus",
        },
        match css {
            CSSPreProcessors::Sass => "^1.69.5",
            CSSPreProcessors::Less => "^4.1.1",
            CSSPreProcessors::Stylus => "^0.62.0",
        },
        DependenciesMod::Dev,
    )?;

    // 处理hook相关逻辑
    let hook = creation_options._hook;
    match hook {
        Hook::AHooks => {
            add_dependency_to_package_json(
                &project_dir,
                "ahooks",
                "^3.7.8",
                
                DependenciesMod::Prod,
            )?;
        }
        Hook::CustomizeHook => {
            copy_hook_to_project(&project_dir)?;
        }
        Hook::AHooksAndCustomize => {
            add_dependency_to_package_json(
                &project_dir,
                "ahooks",
                "^3.7.8",
                
                DependenciesMod::Prod,
            )?;
            copy_hook_to_project(&project_dir)?;
        }
    }
    // 处理状态管理器相关逻辑
    let state = creation_options.state;
    match state {
        StateManagement::Redux => {
            add_dependency_to_package_json(
                &project_dir,
                "@reduxjs/toolkit",
                "^2.0.1",
                
                DependenciesMod::Prod,
            )?;
            add_dependency_to_package_json(
                &project_dir,
                "react-redux",
                "^9.0.4",
                DependenciesMod::Prod,
            )?;
            update_state_management_by_choose(&project_dir,"redux")?;
            // TODO: 还需要处理 相关的配置
            // https://github.com/reduxjs/redux-templates/tree/master/packages/vite-template-redux/src
        }
        StateManagement::Zustand => {
            add_dependency_to_package_json(
                &project_dir,
                "zustand",
                "^4.4.7",
                DependenciesMod::Prod,
            )?;
        }
         StateManagement::Recoil => {
            // 初始化git仓库
            add_dependency_to_package_json(
                &project_dir,
                "recoil",
                "^3.7.8",
                
                DependenciesMod::Prod,
            )?;
            update_state_management_by_choose(&project_dir,"recoil")?;
        }
         StateManagement::Jotai => {
            // 初始化git仓库
            add_dependency_to_package_json(
                &project_dir,
                "jotai",
                "^3.7.8",
                
                DependenciesMod::Prod,
            )?;
        }
         StateManagement::MobX => {
            // 初始化git仓库
            add_dependency_to_package_json(
                &project_dir,
                "mobx",
                "^3.7.8",
                
                DependenciesMod::Prod,
            )?;
        }
         StateManagement::Valtio => {
            // 初始化git仓库
            add_dependency_to_package_json(
                &project_dir,
                "valtio",
                "^3.7.8",
                
                DependenciesMod::Prod,
            )?;
        }
    }

    // 初始化git仓库
    logger::command_msg("git init");

    let git_init = std::process::Command::new("git")
        .current_dir(&project_dir)
        .arg("init")
        .stdout(std::process::Stdio::null())
        .status()
        .expect("failed to execute process");

    if !git_init.success() {
        logger::error("执行`git init`失败");
        std::process::exit(1);
    }


    // 检查是否配置了 Git 用户名和邮箱，如果没有配置，则要求输入，或者在命令行模式下失败。
    logger::command_msg("git config user.name");
    let git_config_user_name = git::check_config(&project_dir, "user.name");
    if !git_config_user_name {
        logger::message("You do not have a git user name set.");

        // 如果在仅命令行模式下创建，不要求输入新的 user.name，
        // 只需告诉用户如何设置并以状态码 1 退出
        if creation_options.cli_mode {
            logger::error("在非交互模式下运行，并且未设置 Git user.name。\n你可以使用以下命令设置：\n\t`git config --global user.name <name>`");
            std::process::exit(1);
        }

        let mut valid_user_name = false;
        let mut invalid_input = false;

        while !valid_user_name {
            let prompt_message = if invalid_input {
                "选择提交时要使用的用户名："
            } else {
                "选择提交时要使用的用户名："
            };
            let input: String = Input::new().with_prompt(prompt_message).interact()?;

            logger::command_msg(&format!("git config user.name {:#?}", &input));

            if !input.is_empty()
                && git::set_config(&project_dir, "user.name", &input)
                && git::check_config(&project_dir, "user.name")
            {
                valid_user_name = true;
            } else {
                invalid_input = true;
            }
        }
    }

    logger::command_msg("git config user.email");
    let git_config_user_email = git::check_config(&project_dir, "user.email");
    if !git_config_user_email {
        logger::message("你尚未设置 Git 用户邮箱。");

        // if being created in cli-only mode, don't ask for a new user.email,
        // just tell them how to set it and exit with status code 1
        if creation_options.cli_mode {
            logger::error("在非交互模式下运行，并且未设置 Git user.email。\n你可以使用以下命令设置：\n\t`git config --global user.email <email>`");
            std::process::exit(1);
        }

        let mut valid_user_email = false;
        let mut invalid_input = false;

        while !valid_user_email {
            let prompt_message = if invalid_input {
                "选择提交时要使用的邮箱："
            } else {
                "选择提交时要使用的邮箱："
            };
            let input: String = Input::new().with_prompt(prompt_message).interact()?;

            logger::command_msg(&format!("配置git的user.email为 {:#?}", &input));

            if !input.is_empty()
                && git::set_config(&project_dir, "user.email", &input)
                && git::check_config(&project_dir, "user.email")
            {
                valid_user_email = true;
            } else {
                invalid_input = true;
            }
        }
    }

    logger::command_msg("git add -A");

    let git_add = std::process::Command::new("git")
        .current_dir(&project_dir)
        .arg("add")
        .arg("-A")
        .stdout(std::process::Stdio::null())
        .status()
        .expect("failed to execute process");

    if !git_add.success() {
        logger::error("执行`git add -A`失败");
        std::process::exit(1);
    }

    logger::command_msg("git commit -m Initial commit");

    let git_commit = std::process::Command::new("git")
        .current_dir(&project_dir)
        .arg("commit")
        .arg("-m")
        .arg("Initial commit")
        .stdout(std::process::Stdio::null())
        .status()
        .expect("failed to execute process");

    if !git_commit.success() {
        logger::error("执行`git commit`失败");
        std::process::exit(1);
    }

    logger::command_msg("git branch -M main");

    let git_branch = std::process::Command::new("git")
        .current_dir(&project_dir)
        .arg("branch")
        .arg("-M")
        .arg("main")
        .stdout(std::process::Stdio::null())
        .status()
        .expect("failed to execute process");

    if !git_branch.success() {
        logger::error("执行`git branch -M main`失败");
        std::process::exit(1);
    }

    Ok(())
}

/// 检查CLI工具的版本
/// 定义了一个名为check_cli_version的函数，
/// 通过比较当前版本与Crates.io注册表上的最新版本来检查CLI工具是否是最新的。
/// 该函数使用update_informer crate执行版本检查。
pub fn check_cli_version() -> Result<()> {
    // 从由Cargo设置的环境变量中获取CLI工具的名称和版本。
    let name = env!("CARGO_PKG_NAME");
    let version = env!("CARGO_PKG_VERSION");

    // 使用Crates.io注册表、工具的名称和版本创建一个更新通知器。
    let informer = update_informer::new(registry::Crates, name, version)
        .timeout(Duration::from_secs(2))
        .interval(Duration::ZERO);

    // 尝试检查新版本。如果成功，将其与当前版本进行比较。
    if let Some(new_version) = informer.check_version().ok().flatten() {
        // 打印一条消息，指示当前版本已过时。
        logger::message(&style(&format!("你正在运行 `{name}` 版本 {version}，该版本落后于最新发布的版本 ({new_version})。")).yellow().to_string());

        // 提供使用cargo更新CLI工具的建议。
        logger::message(&format!(
            "如果想更新到最新版本, 尝试: {}",
            style("cargo install --force f_cli").yellow()
        ));
    } else {
        // 如果版本检查失败或超时，打印一条消息，指示CLI工具是最新的。
        logger::message(&format!("该CLI版本已为最新:v{version},无需更新"));
    }

    // 返回一个表示成功或失败的Result。
    Ok(())
}

